/*	
 *				
 *					ISNW ---- It is a war.It is not warcraft!
 *
 *
 *					Author:					zhoukeli(haxixi_keli)
 *					Filename:				aabbox3d.h
 *					CreateTime:				2008/12/10
 *					ModifyTime:				2008/12/10
 *
 */

#ifndef __ISNW_AABBOX_H__
#define __ISNW_AABBOX_H__

#include "isnwTypes.h"
#include "line3d.h"
#include "plane3d.h"

namespace ISNW
{
namespace core
{
	template<typename T>
	class aabbox3d
	{
	public:
		//! constructor
		aabbox3d():minEdge(-1,-1,-1),maxEdge(1,1,1){}
		aabbox3d(const vector3d<T>& min,const vector3d<T>& max):minEdge(min),maxEdge(max){}
		aabbox3d(const vector3d<T>& init):minEdge(init),maxEdge(init){}
		aabbox3d(T mx,T my,T mz,T Mx,T My,T Mz){set(mx,my,mz,Mx,My,Mz);}

	public:
		//! operator
		aabbox3d<T>& operator = (const aabbox3d<T>& other){minEdge=other.minEdge;maxEdge=other.maxEdge;return*this;}

		aabbox3d<T>& operator += (const vector3d<T>& point){return addpoint(point);}
		aabbox3d<T> operator + (const vector3d<T>& point) const {aabbox3d<T> ret = *this;ret.addpoint(point);return ret;}
		
		aabbox3d<T>& operator += (const aabbox3d<T>& other)
		{addpoint(other.minEdge);addpoint(other.maxEdge);return *this;}
		aabbox3d<T> operator + (const aabbox3d<T>& other) const
		{aabbox3d<T> ret = *this;ret.addpoint(other.maxEdge);ret.addpoint(other.minEdge);return ret;}

		bool operator == (const aabbox3d<T>& other) const {return (minEdge==other.minEdge&&maxEdge==other.maxEdge);}

	public:
		aabbox3d<T>& addpoint(const vector3d<T>& point)
		{
			if(point.x < minEdge.x){minEdge.x = point.x;}
			if(point.y < minEdge.y){minEdge.y = point.y;}
			if(point.z < minEdge.z){minEdge.z = point.z;}

			if(point.x > maxEdge.x){maxEdge.x = point.x;}
			if(point.y > maxEdge.y){maxEdge.y = point.y;}
			if(point.z > maxEdge.z){maxEdge.z = point.z;}

			return *this;
		}

		aabbox3d<T>& addbox(const aabbox3d<T>& box)
		{
			addpoint(box.maxEdge);
			addpoint(box.minEdge);

			return *this;
		}

		aabbox3d<T>& reset(const vector3d<T>& point)
		{
			minEdge.set(point);
			maxEdge.set(point);

			return *this;
		}

		aabbox3d<T>& reset(const aabbox3d<T>& other)
		{
			*this = other;
		}

		aabbox3d<T>& set(T mx,T my, T mz, T Mx,T My, T Mz)
		{
			minEdge.set(mx,my,mz);
			maxEdge.set(Mx,My,Mz);

			return *this;
		}

		aabbox3d<T>& set(core::vector3d<T> m,core::vector3d<T> M)
		{
			minEdge = m;
			maxEdge = M;

			return *this;
		}

		bool ispointinside(const vector3d<T>& point) const
		{
			if(point >= minEdge && point <= maxEdge) return true;

			return false;
		}

		bool ispointtotalinside(const vector3d<T>& point) const
		{
			if(point > minEdge && point < maxEdge) return true;

			return false;
		}

		bool isintersectwithbox(const aabbox3d<T>& other) const
		{
			return (minEdge < other.maxEdge && maxEdge > other.minEdge);
		}

		bool isfullinsidebox(const aabbox3d<T>& other) const
		{
			return (minEdge >= other.minEdge && maxEdge <= other.maxEdge);
		}

		bool isfullcontainbox(const aabbox3d<T>& other) const
		{
			return (minEdge <= other.minEdge && maxEdge >= other.maxEdge);
		}

		//! minEdge <= point + t*direction <= maxEdge
		bool getintersectionwithline(const vector3d<T>& linepoint,
					const vector3d<T>& direction,
					vector3d<T>& out1,
					vector3d<T>& out2) const
		{
			vector3d<T> left = minEdge - linepoint;
			vector3d<T> right = maxEdge - linepoint;

			left /= direction;
			right /= direction;

			T t_left_max = core::_max_<T>(left.x,left.y,left.z);
			T t_rght_min = core::_min_<T>(right.x,right.y,right.z);
			if(t_left_max > t_rght_min) return false;

			if(direction.x == 0)
				if(left.x > 0 || right.x < 0) return false;

			if(direction.y == 0)
				if(left.y > 0 || right.y < 0) return false;

			if(direction.z == 0)
				if(left.z > 0 || right.z < 0) return false;

			out1 = t_left_max * direction + linepoint;
			out2 = t_rght_min * direction + linepoint;

			return true;
		}

		bool getintersectionwithline(const vector3d<T>& linepoint,
					const vector3d<T>& direction,
					vector3d<T>* pout1,
					vector3d<T>* pout2) const
		{
			vector3d<T> tmp1,tmp2;
			if(getintersectionwithline(linepoint,direction,tmp1,tmp2))
			{
				if(pout1) *pout1 = tmp1;
				if(pout2) *pout2 = tmp2;

				return true;
			}
			return false;
		}

		bool getintersectionwithlimitedline(const line3d<T> line) const
		{
			vector3d<T> tmp1,tmp2;
			if(getintersectionwithline(line.getstartpoint(),line.getvector(),tmp1,tmp2))
				if(line.ispointin(tmp1) && line.ispointin(tmp2)) return true;

			return false;
		}

		aabbox3d<T>& repair()
		{
			T tmp = 0;
			if(maxEdge.x < minEdge.x){tmp = minEdge.x;minEdge.x = maxEdge.x;maxEdge.x = tmp;}
			if(maxEdge.y < minEdge.y){tmp = minEdge.y;minEdge.y = maxEdge.y;maxEdge.y = tmp;}
			if(maxEdge.z < minEdge.z){tmp = minEdge.z;minEdge.z = maxEdge.z;maxEdge.z = tmp;}

			return *this;
		}

		vector3d<T> getcenter() const
		{
			return vector3d<T>((minEdge + maxEdge)*0.5);
		}

		vector3d<T> getdiag() const
		{
			return (maxEdge - minEdge);
		}

		E_INTERSECTION_RELATION getrelationwithplane(const plane3d<T> plane) const
		{
			vector3d<T> nearpoint(maxEdge);
			vector3d<T> farpoint(minEdge);

			if(plane.normal.x > 0)
			{
				nearpoint.x = minEdge.x;
				farpoint.x = maxEdge.x;
			}

			if(plane.normal.y > 0)
			{
				nearpoint.y = minEdge.y;
				farpoint.y = maxEdge.y;
			}

			if(plane.normal.z > 0)
			{
				nearpoint.z = minEdge.z;
				farpoint.z = maxEdge.z;
			}

			if(plane.getrelationwithpoint(nearpoint) == PLANE_FRONT) return PLANE_FRONT;
			if(plane.getrelationwithpoint(farpoint) == PLANE_BACK) return PLANE_BACK;

			return PLANE_INTERSECT;
		}

		bool isempty(){return minEdge == maxEdge;}

	public:
		vector3d<T> minEdge;
		vector3d<T> maxEdge;
	};

	//! typedef for float axis-aligned-bound-box
	typedef aabbox3d<f32> aabbox3df;
	//! typedef for int axis-aligned-bound-box
	typedef aabbox3d<s32> aabbox3di;

} // end namespace core
} // end namespace ISNW
#endif